using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Text;
using System.Xml;

using System.Drawing;
using System.Drawing.Drawing2D;
using System.ComponentModel;

using Athena.Core;

namespace Athena.Objects
{
    [Serializable()]
    public class SpeciesGlyph : IGlyph
    {
        public override object Clone()
        {
            return new SpeciesGlyph(this);
        }
        public SpeciesGlyph(SpeciesGlyph copy)
        {
            TextV = copy.TextV;
            TextH = copy.TextH;

            if (copy._Item != null)
                Item = (Item)copy._Item.Clone();

            _oShape = copy._oShape;
            _Location = copy.Location;
            Dimensions = copy.Dimensions;
            Text = copy.Text;
            LabelBounds = copy.LabelBounds;
            TextColor = copy.TextColor;
            ColorStart = copy.ColorStart;
            ColorEnd = copy.ColorEnd;
            TextFont = copy.TextFont;
            ShowText = copy.ShowText;            
            Type = copy.Type;
            EdgeColor = copy.EdgeColor;
            EdgeThickness = copy.EdgeThickness;
            ColorStart = copy.ColorStart;
            Immobile = copy.Immobile;
            _IncomingConnections = new List<Connector>();
            _OutgoingConnections = new List<Connector>();
            /*if (copy.Degradation != null)
            {
                _Degradation = copy._Degradation.Clone() as DegradationReaction;
                _Degradation.TargetSpecies = this;
            }*/

            for (int i = 0; i < copy.AnchoredItems.Count; ++i)
            {
                if (copy.AnchoredItems[i] is Decorator)
                {
                    Decorator decorator = (Decorator)copy.AnchoredItems[i];
                    Decorator oNew = (Decorator)decorator.Clone();
                    oNew.Location = new PointF(decorator.Location.X - copy.Location.X + Location.X,
                    decorator.Location.Y - copy.Location.Y + Location.Y);
                    //oNew.ParentGlyph = this;
                    oNew.AnchoredSource = this;
                    oNew.DoneModifying(Corner.None,true);
                }
            }

            Name = copy.Name;
            Parent = null;          
        }
        /*protected DegradationReaction _Degradation;
        public DegradationReaction Degradation
        {
            get { return _Degradation; }
            set 
            {
                if (_Degradation != value && _Degradation != null)
                    RemoveConnector(_Degradation);
                if (value != null)
                {
                    _Degradation = value;
                    if (_Degradation.TargetSpecies != this)
                        _Degradation.TargetSpecies = (this);

                    AddOutputConnection(_Degradation);
                }
            }
        }*/

        public override void Transform(PointF oDirection, ref Corner oCorner, bool Ctrl)
        {
            base.Transform(oDirection, ref oCorner, Ctrl);            
            /*if (Degradation == null) return;
            float x = Degradation.Location.X, y = Degradation.Location.Y;
            switch (oCorner)
            {
                case Corner.BottomLeft:
                    {
                        if (x < _Location.X)
                            x -= oDirection.X;
                        if (y > _Location.Y)
                            y += oDirection.Y;
                    }
                    break;
                case Corner.BottomRight:
                    {
                        if (x > _Location.X)
                            x += oDirection.X;
                        if (y > _Location.Y)
                            y += oDirection.Y;
                    }
                    break;
                case Corner.TopLeft:
                    {
                        if (x < _Location.X)
                            x -= oDirection.X;
                        if (y < _Location.Y)
                            y -= oDirection.Y;
                    }
                    break;
                case Corner.TopRight:
                    {
                        if (x > _Location.X)
                            x += oDirection.X;
                        if (y < _Location.Y)
                            y -= oDirection.Y;
                    }
                    break;
            }
            Degradation.Location = new PointF(x, y);  */          
        }
        /// <summary>
        /// Initializes a new instance of the SpeciesGlyph class.
        /// </summary>
        /// <param name="location"></param>
        public SpeciesGlyph(PointF location)
        {
            TextH = HorizontalAlignment.Center;
            TextV = VerticalAlignment.Middle;

            _Location = location;
            _OldLocation = _Location;
            _Dimensions = new SizeF(60,46);
            Item =new Species("protein", "S0", 0);
            
            _oFillColorStart = Color.White;
            _oFillColorEnd = Color.SkyBlue;
            _oShape = SHAPE.RoundedRectangle;
            _oEdgeColor = Color.DarkGray;
            _dEdgeThickness = 1.25f;

            LoadDefaults(DEFAULT);
        }

        [CategoryAttribute("Species Attributes"), Description("Concentration of this species")]
        public virtual double Concentration
        {
            get 
            {
                if (_Item is Species)
                    return (_Item as Species).Concentration;
                else
                    return 0;
            }
            set 
            {
                if (_Item is Species)
                    (_Item as Species).Concentration = value; 
            }
        }

        [CategoryAttribute("Species Attributes"), Description("Indicates whether or not this species remains constant")]
        public virtual bool Boundary
        {
            get 
            {
                if (_Item is Species)
                {
                    Module mod = _Item.ParentModule;
                    if (mod != null && mod.Drawable != null && mod.Drawable.IsSelected)
                        return (_Item as Species).Boundary;
                    
                    return (_Item as Species).GetBoundary(Program.Model.BackEndModel);
                }
                else
                    return false;
            }
            set 
            {
                if (_Item is Species)
                    (_Item as Species).Boundary = value; 
            }
        }

        public override void ShowToolTip(ToolTip tooltip, Control pnlCanvas, PointF location)
        {
            tooltip.ToolTipTitle = (Boundary ? "Boundary" : "Floating") + " species:";
            tooltip.SetToolTip(pnlCanvas,
                String.Format(
                            "{2}ID:\t{0}"
                          + "{2}Concentration:\t{1}",
                Name,
                Concentration,
                "\r\n"));
        }

        public SpeciesGlyph()
        {
            _Location = new PointF(0, 0);
            _Dimensions = new SizeF(30, 30);

            Item = new Species("protein", "S0", 0);
            _oFillColorStart = Color.White;
            _oFillColorEnd = Color.SkyBlue;
            _oShape = SHAPE.RoundedRectangle;
            _oEdgeColor = Color.DarkGray;
            _dEdgeThickness = 1.25f;
            _Item = new Species();

            LoadDefaults(DEFAULT);
        }

        public enum SHAPE
        {
            Box,
            Ellipse,
            Empty,
            RoundedRectangle,
            Circle
        }

        

        private SHAPE _oShape = SHAPE.Ellipse;

        [Description("Allows to choose the Shape drawn for this SimpleGlyph"), Category("Species Attributes")]
        public SHAPE Shape
        {
            get { return _oShape; }
            set { _oShape = value; }
        }

        public override Bitmap GetPreview(int width, int height)
        {
            Bitmap oBitmap = new Bitmap(width, height);
            Graphics g = Graphics.FromImage(oBitmap);
            
            PointF oldLocation = Location;
            SizeF oldDimensions = Dimensions;
            float oldEdgeThickness = EdgeThickness;
            
            Location = new PointF(1f, 2f);
            EdgeThickness = 1.5f;
            Dimensions = new SizeF(width - 4f, height - 4f);
            RenderGlyph(g);

            Location = oldLocation;
            Dimensions = oldDimensions;
            EdgeThickness = oldEdgeThickness;

            return oBitmap;
        }

        public override IDrawable.ViewType View
        {
            get
            {
                return base.View;
            }
            set
            {
                base.View = value;
                if (value == ViewType.Schematic)
                {
                    //_oLabelColor = Color.Black;
                    float size = Math.Min(Bounds.Height, Bounds.Width);
                    _oLabelFont = new Font(
                        _oLabelFont.FontFamily,
                        size / 2,
                        _oLabelFont.Style,
                        GraphicsUnit.Point,
                        _oLabelFont.GdiCharSet,
                        _oLabelFont.GdiVerticalFont);
                }
                else
                {
                    //_oLabelColor = Color.Blue;
                    //float size = Math.Min(Bounds.Height, Bounds.Width);
                    _oLabelFont = new Font(
                        _oLabelFont.FontFamily,
                        8.25f,
                        _oLabelFont.Style,
                        GraphicsUnit.Point,
                        _oLabelFont.GdiCharSet,
                        _oLabelFont.GdiVerticalFont);
                }
                Utility.Refresh();
            }
        }

        
        public override SBMLExtension.EmlRenderExtension.LocalStyle ToStyle(SBMLExtension.EmlRenderExtension.RenderInformation oInfo)
        {
            SBMLExtension.EmlRenderExtension.LocalStyle oResult = new SBMLExtension.EmlRenderExtension.LocalStyle();
            SBMLExtension.EmlRenderExtension.Group oGroup = new SBMLExtension.EmlRenderExtension.Group();
            oGroup.Stroke = oInfo.AddColorDefinition(_oEdgeColor);
            oGroup.StrokeWidth = _dEdgeThickness.ToString();
            if (_oFillColorStart == _oFillColorEnd)
                oGroup.Fill = oInfo.AddColorDefinition(_oFillColorStart);
            else
                oGroup.Fill = oInfo.AddLinearGradientDefinition(_oFillColorStart, _oFillColorEnd);

            switch (Shape)
            {
                case SHAPE.Box:
                    oGroup.Children.Add(new SBMLExtension.EmlRenderExtension.Rectangle());
                    break;
                case SHAPE.Ellipse:
                case SHAPE.Circle:
                    {
                        RectangleF oGlyphRect = Bounds;
                        SBMLExtension.EmlRenderExtension.Ellipses ellipse = new SBMLExtension.EmlRenderExtension.Ellipses();
                        ellipse.CX = (oGlyphRect.Width / 2f).ToString();
                        ellipse.CY = (oGlyphRect.Height / 2f).ToString();
                        ellipse.RX = (oGlyphRect.Width / 2f).ToString();
                        ellipse.RY = (oGlyphRect.Height / 2f).ToString();
                        oGroup.Children.Add(ellipse);

                        if (ShowCloneMarker)
                        {
                            AddCloneMarkerGradient(oInfo);

                            SBMLExtension.EmlRenderExtension.Ellipses ellipse2 = new SBMLExtension.EmlRenderExtension.Ellipses();
                            ellipse2.CX = (oGlyphRect.Width / 2f).ToString();
                            ellipse2.CY = (oGlyphRect.Height / 2f).ToString();
                            ellipse2.RX = (oGlyphRect.Width / 2f).ToString();
                            ellipse2.RY = (oGlyphRect.Height / 2f).ToString();

                            
                            ellipse2.Fill = "cloneMarker";
                            oGroup.Children.Add(ellipse2);
                        }


                    }
                    break;
                case SHAPE.Empty:                                      
                    {
                        SBMLExtension.EmlRenderExtension.Group oEmptyGroup = new SBMLExtension.EmlRenderExtension.Group();
                        RectangleF oGlyphRect = Bounds;
                        SBMLExtension.EmlRenderExtension.Ellipses ellipse = new SBMLExtension.EmlRenderExtension.Ellipses();
                        ellipse.CX = (oGlyphRect.Width / 2f).ToString();
                        ellipse.CY = (oGlyphRect.Height / 2f).ToString();
                        ellipse.RX = (oGlyphRect.Width / 2f).ToString();
                        ellipse.RY = (oGlyphRect.Height / 2f).ToString();
                        oGroup.Children.Add(ellipse);

                        SBMLExtension.EmlRenderExtension.Curve oCurve = new SBMLExtension.EmlRenderExtension.Curve();
                        SBMLExtension.LayoutExtension.Point point1 = new SBMLExtension.LayoutExtension.Point(0f, 100f);
                        point1.IsRelativeX = true;
                        point1.IsRelativeY = true;
                        SBMLExtension.LayoutExtension.Point point2 = new SBMLExtension.LayoutExtension.Point(100f, 0f);
                        point2.IsRelativeX = true;
                        point2.IsRelativeY = true;
                        oCurve.CurveSegments.Add(new SBMLExtension.LayoutExtension.CurveSegment(point1, point2));
                        oEmptyGroup.Children.Add(oCurve);                         
                        
                        oGroup.Children.Add(oEmptyGroup);
                    }
                    
                    break;
                case SHAPE.RoundedRectangle:
                default:
                    oGroup.Children.Add(new SBMLExtension.EmlRenderExtension.Rectangle("10%"));

                    if (ShowCloneMarker)
                    {
                        AddCloneMarkerGradient(oInfo);

                        SBMLExtension.EmlRenderExtension.Rectangle rect = new SBMLExtension.EmlRenderExtension.Rectangle("10%");
                        rect.Fill = "cloneMarker";
                        oGroup.Children.Add(rect);

                        AddCloneMarkerText(oInfo, oGroup);

                    }

                    break;
            }

            AddDecoratorsToGroup(oInfo, oGroup);

            AddLabelGlyph(oInfo, oGroup);

            oResult.Group = oGroup;
            return oResult;
        }

        public override void RenderGlyph(Graphics g)
        {
            Pen oOutline = new Pen(_oEdgeColor, _dEdgeThickness);

            Brush oFilled;
            if (View == ViewType.Schematic)
            {
                oFilled = new SolidBrush(Color.White);
            }
            else
            {
                if (_oFillColorStart == _oFillColorEnd || _Dimensions.Width < 2 || _Dimensions.Height < 2)
                    oFilled = new SolidBrush(_oFillColorStart);
                else
                    oFilled = new LinearGradientBrush(Location, new PointF(Location.X + Dimensions.Width, Location.Y + Dimensions.Height), _oFillColorStart, _oFillColorEnd);
            }
            RectangleF oTemp = Bounds, oTempSmall = Bounds;

            
            switch (Shape)
            {
                case SHAPE.Empty:
                    {
                        GraphicsPath oPath = new GraphicsPath();

                        oPath.AddEllipse(0, 0, 10, 10);
                        oPath.AddLine(0, 10, 10, 0);
                        oPath.CloseFigure();

                        RectangleF pathBounds = oPath.GetBounds();
                        Matrix oTransform = new Matrix();

                        oTransform.Scale(
                            _Dimensions.Width / pathBounds.Width,
                            _Dimensions.Height / pathBounds.Height, MatrixOrder.Prepend);
                        oTransform.Translate(_Location.X - pathBounds.X * _Dimensions.Width / pathBounds.Width, _Location.Y - pathBounds.Y * _Dimensions.Height / pathBounds.Height, MatrixOrder.Append);

                        oPath.Transform(oTransform);
                        if (_ViewType != ViewType.Schematic)
                            g.FillPath(oFilled, oPath);
                        g.DrawPath(oOutline, oPath);

                    }
                    break;

                case SHAPE.Box:
                    if (Boundary)
                    {
                        oTemp.Inflate(2 * _dEdgeThickness, 2 * _dEdgeThickness);
                        g.DrawRectangle(oOutline, oTemp.X, oTemp.Y, oTemp.Width, oTemp.Height);
                        oTemp.Inflate(-3 * _dEdgeThickness, -3 * _dEdgeThickness);
                    }

                    oTempSmall.Inflate(-_dEdgeThickness / 2, -_dEdgeThickness / 2);
                    if (_ViewType != ViewType.Schematic)
                        g.FillRectangle(oFilled, oTempSmall);
                    if (ShowCloneMarker)
                    {

                        LinearGradientBrush oBrush = new LinearGradientBrush(Bounds, Color.Transparent, CloneMarkerColor, LinearGradientMode.Vertical);
                        ColorBlend oBlend = new ColorBlend(2);
                        oBlend.Positions = new float[] { 0f, 1f - ClonePercentage, 1f - ClonePercentage, 1f };
                        oBlend.Colors = new Color[] { Color.Transparent, Color.Transparent, CloneMarkerColor, CloneMarkerColor };
                        oBrush.InterpolationColors = oBlend;
                        g.FillRectangle(oBrush, oTempSmall);
                    }

                    DrawCloneMarkerText(g);

                    g.DrawRectangle(oOutline, oTemp.X, oTemp.Y, oTemp.Width, oTemp.Height);
                    break;
                case SHAPE.Circle:
                    float fDiameter = Math.Min(oTemp.Width, oTemp.Height);
                    oTemp.X = oTemp.X + (oTemp.Width - fDiameter) / 2f;
                    oTemp.Y = oTemp.Y + (oTemp.Height - fDiameter) / 2f;
                    oTempSmall = oTemp;
                    if (Boundary)
                    {
                        oTemp.Inflate(2 * _dEdgeThickness, 2 * _dEdgeThickness);
                        g.DrawEllipse(oOutline, oTemp.X, oTemp.Y, fDiameter, fDiameter);
                        oTemp.Inflate(-3 * _dEdgeThickness, -3 * _dEdgeThickness);
                    }
                    oTempSmall.Inflate(-_dEdgeThickness / 2, -_dEdgeThickness / 2);
                    if (_ViewType != ViewType.Schematic)
                        g.FillEllipse(oFilled, oTempSmall.X, oTempSmall.Y, fDiameter - _dEdgeThickness / 2f, fDiameter - _dEdgeThickness / 2f);
                    
                    if (ShowCloneMarker)
                    {

                        LinearGradientBrush oBrush = new LinearGradientBrush(Bounds, Color.Transparent, CloneMarkerColor, LinearGradientMode.Vertical);
                        ColorBlend oBlend = new ColorBlend(2);
                        oBlend.Positions = new float[] { 0f, 1f - ClonePercentage, 1f - ClonePercentage, 1f };
                        oBlend.Colors = new Color[] { Color.Transparent, Color.Transparent, CloneMarkerColor, CloneMarkerColor };
                        oBrush.InterpolationColors = oBlend;

                        g.FillEllipse(oBrush, oTempSmall.X, oTempSmall.Y, fDiameter - _dEdgeThickness / 2f, fDiameter - _dEdgeThickness / 2f);

                        DrawCloneMarkerText(g);
                    }
                    
                    g.DrawEllipse(oOutline, oTemp.X, oTemp.Y, fDiameter, fDiameter);
                    

                    break;


                case SHAPE.Ellipse:
                    if (Boundary)
                    {
                        oTemp.Inflate(2 * _dEdgeThickness, 2 * _dEdgeThickness);
                        g.DrawEllipse(oOutline, oTemp.X, oTemp.Y, oTemp.Width, oTemp.Height);
                        oTemp.Inflate(-3 * _dEdgeThickness, -3 * _dEdgeThickness);
                    }
                    oTempSmall.Inflate(-_dEdgeThickness / 2, -_dEdgeThickness / 2);
                    if (_ViewType != ViewType.Schematic)
                        g.FillEllipse(oFilled, oTempSmall);

                    if (ShowCloneMarker)
                    {

                        LinearGradientBrush oBrush = new LinearGradientBrush(Bounds, Color.Transparent, CloneMarkerColor, LinearGradientMode.Vertical);
                        ColorBlend oBlend = new ColorBlend(2);
                        oBlend.Positions = new float[] { 0f, 1f - ClonePercentage, 1f - ClonePercentage, 1f };
                        oBlend.Colors = new Color[] { Color.Transparent, Color.Transparent, CloneMarkerColor, CloneMarkerColor };
                        oBrush.InterpolationColors = oBlend;

                        g.FillEllipse(oBrush, oTempSmall);

                        DrawCloneMarkerText(g);
                    }

                    g.DrawEllipse(oOutline, oTemp.X, oTemp.Y, oTemp.Width, oTemp.Height);

                    break;
                case SHAPE.RoundedRectangle:
                    {
                        GraphicsPath oPath = new GraphicsPath();
                        Utility.GenerateRoundedRectangle(oPath, oTemp);
                        RectangleF pathBounds = oPath.GetBounds();
                        Matrix oTransform = new Matrix();

                        if (Boundary)
                        {
                            oTemp.Inflate(2 * _dEdgeThickness, 2 * _dEdgeThickness);

                            GraphicsPath oPath2 = new GraphicsPath();
                            Utility.GenerateRoundedRectangle(oPath2, oTemp);
                            RectangleF pathBounds2 = oPath2.GetBounds();
                            Matrix oTransform2 = new Matrix();
                            oTransform2.Scale(
                            oTemp.Width / pathBounds2.Width,
                            oTemp.Height / pathBounds2.Height, MatrixOrder.Prepend);
                            oTransform2.Translate(oTemp.X - pathBounds2.X * oTemp.Width / pathBounds2.Width, oTemp.Y - pathBounds2.Y * oTemp.Height / pathBounds2.Height, MatrixOrder.Append);
                            oPath2.Transform(oTransform2);

                            g.DrawPath(oOutline, oPath2);

                            oTemp.Inflate(-3 * _dEdgeThickness, -3 * _dEdgeThickness);
                        }

                        oTransform.Scale(
                            oTemp.Width / pathBounds.Width,
                            oTemp.Height / pathBounds.Height, MatrixOrder.Prepend);
                        oTransform.Translate(oTemp.X - pathBounds.X * oTemp.Width / pathBounds.Width, oTemp.Y - pathBounds.Y * oTemp.Height / pathBounds.Height, MatrixOrder.Append);

                        oPath.Transform(oTransform);
                        if (_ViewType != ViewType.Schematic)
                            g.FillPath(oFilled, oPath);

                        if (ShowCloneMarker)
                        {

                            LinearGradientBrush oBrush = new LinearGradientBrush(Bounds, Color.Transparent, CloneMarkerColor, LinearGradientMode.Vertical);
                            ColorBlend oBlend = new ColorBlend(2);
                            oBlend.Positions = new float[] { 0f, 1f - ClonePercentage, 1f - ClonePercentage, 1f };
                            oBlend.Colors = new Color[] { Color.Transparent, Color.Transparent, CloneMarkerColor, CloneMarkerColor };
                            oBrush.InterpolationColors = oBlend;

                            g.FillPath(oBrush, oPath);

                            DrawCloneMarkerText(g);
                        }

                        g.DrawPath(oOutline, oPath);

                        
                    }
                    break;
            }
        }
        /// <summary>
        /// writes the iglyph attributes to xml
        /// </summary>
        /// <param name="writer"></param>
        public override void WriteXML(System.Xml.XmlWriter writer)
        {
            base.WriteXML(writer);

            writer.WriteStartElement("SpeciesGlyph", "");

            writer.WriteStartElement("Shape");
            writer.WriteValue(_oShape - SHAPE.Box);
            writer.WriteEndElement();

            writer.WriteEndElement();
        }
        /// <summary>
        /// reads the iglyph attributes from xml
        /// </summary>
        /// <param name="writer"></param>
        public override bool ReadXML(XmlReader reader)
        {
            bool read = base.ReadXML(reader);
            read = reader.Read();

            if (!read) return false;
            
            if (reader.NodeType == XmlNodeType.Element && reader.Name == "SpeciesGlyph")
            {
                read = reader.Read();
                while (read)
                {
                    if (reader.NodeType == XmlNodeType.EndElement)
                        break;

                    if (reader.NodeType == XmlNodeType.Element)
                    {
                        switch (reader.Name)
                        {
                            case "Shape":
                                read = reader.Read();
                                if (reader.NodeType == XmlNodeType.Text)
                                    _oShape = SHAPE.Box + reader.ReadContentAsInt();
                                
                                break;
                            default:
                                while (reader.NodeType != XmlNodeType.EndElement)
                                    read = reader.Read();
                                break;
                        }
                    }

                    if (!read || (reader.NodeType == XmlNodeType.EndElement && reader.Name == "SpeciesGlyph")) break;

                    if (reader.NodeType != XmlNodeType.Element)
                        read = reader.Read();
                }
                return true;
            }
            return false;
        }

        public override void LoadDefaults(IDrawable drawable)
        {
            SpeciesGlyph Default = drawable as SpeciesGlyph;
            if (Default != null)
            {
                base.LoadDefaults(drawable);
                Shape = DEFAULT.Shape;
            }
        }

        public static SpeciesGlyph DEFAULT = null;


   }
}
